home *** CD-ROM | disk | FTP | other *** search
- ############################################################################
- #
- # Name: press.icn
- #
- # Title: LZW Compression and Decompression Utility
- #
- # Author: Robert J. Alexander
- #
- # Date: December 5, 1989
- #
- ############################################################################
- #
- # Note: This program is designed primarily to demonstrate the LZW
- # compression process. It contains a lot of tracing toward
- # that end and is too slow for practical use.
- #
- ############################################################################
- #
- # Usage: press [-t] -c [-s n] [-f <compressed file>] <file to compress>...
- # press [-t] -x <compressed file>...
- #
- # -c perform compression
- # -x expand (decompress) compressed file
- # -f output file for compression -- if missing standard output used
- # -s maximum string table size
- # (for compression only -- default = 1024)
- # -t output trace info to standard error file
- #
- # If the specified maximum table size is positive, the string table is
- # discarded when the maximum size is reached and rebuilt (recommended).
- # If negative, the original table is not discarded, which might produce
- # better results in some circumstances.
- #
- ############################################################################
- #
- # Features that might be nice to add someday:
- #
- # Allow decompress output to standard output.
- #
- # Handle heirarchies.
- #
- # Way to list files in archive, and access individual files
- #
- ############################################################################
- #
- # Links: options
- #
- ############################################################################
-
- global inchars,outchars,tinchars,toutchars,lzw_recycles,
- lzw_stringTable,lzw_trace,wr,wrs,rf,wf
-
- link options
-
- procedure main(arg)
- local compr,expand,fn,maxT,maxTableSize,opt,outfile,wfn
-
- #
- # Initialize.
- #
- opt := options(arg,"ts+f:cx")
- if *arg = 0 then arg := ["-"]
- lzw_trace := opt["t"]
- expand := opt["x"]
- compr := opt["c"]
- outfile := opt["f"]
- maxTableSize := \opt["s"]
- if (/expand & /compr) then Usage()
- wr := write ; wrs := writes
- inchars := outchars := tinchars := toutchars := lzw_recycles := 0
- #
- # Process compression.
- #
- if \compr then {
- if \expand then Usage()
- if \outfile then
- wf := open(outfile,"w") | stop("Can't open output file ",outfile)
- #
- # Loop to process files on command line.
- #
- every fn := !arg do {
- if fn === outfile then next
- wr(&errout,"\nFile \"",fn,"\"")
- rf := if fn ~== "-" then open(fn) | &null else &input
- if /rf then {
- write(&errout,"Can't open input file \"",fn,"\" -- skipped")
- next
- }
- write(wf,tail(fn))
- maxT := compress(r,w,maxTableSize)
- close(rf)
- stats(maxT)
- }
- }
- #
- # Process decompression.
- #
- else if \expand then {
- if \(compr | outfile | maxTableSize) then Usage()
- #
- # Loop to process files on command line.
- #
- every fn := !arg do {
- rf := if fn ~== "-" then open(fn) | &null else &input
- if /rf then {
- write(&errout,"Can't open input file \"",fn,"\" -- skipped")
- next
- }
- while wfn := read(rf) do {
- wr(&errout,"\nFile \"",wfn,"\"")
- wf := open(wfn,"w") | &null
- if /wf then {
- write(&errout,"Can't open output file \"",wfn,"\" -- quitting")
- exit(1)
- }
- maxT := decompress(r,w)
- close(wf)
- stats(maxT)
- }
- close(rf)
- }
- }
- else Usage()
- #
- # Write statistics
- #
- wr(&errout,"\nTotals: ",
- "\n input = ",tinchars,
- "\n output = ",toutchars,
- "\n compression factor = ",(real(toutchars) / real(0 < tinchars)) | "")
- end
-
-
- procedure stats(maxTableSize)
- #
- # Write statistics
- #
- wr(&errout,
- " input = ",inchars,
- "\n output = ",outchars,
- "\n compression factor = ",(real(outchars) / real(0 < inchars)) | "",
- "\n table size = ",*lzw_stringTable,"/",maxTableSize,
- " (",lzw_recycles," recycles)")
- tinchars +:= inchars
- toutchars +:= outchars
- inchars := outchars := lzw_recycles := 0
- return
- end
-
-
- procedure r()
- return 1(reads(rf),inchars +:= 1)
- end
-
-
- procedure w(s)
- return 1(writes(wf,s),outchars +:= *s)
- end
-
-
- procedure Usage()
- stop("_
- # Usage: icompress [-t] -c [-s n] <file to compress>...\n_
- # icompress [-t] -x <compressed file>...\n_
- #\n_
- # -c perform compression\n_
- # -x expand (decompress) compressed file\n_
- # -f output file for compression -- if missing standard output used\n_
- # -s maximum string table size\n_
- # (for compression only -- default = 1024)\n_
- # -t output trace info to standard error file\n_
- #")
- end
-
- procedure tail(fn)
- local i
- i := 0
- every i := find("/",fn)
- return fn[i + 1:0]
- end
-
- #
- # compress() -- LZW compression
- #
- # Arguments:
- #
- # inproc a procedure that returns a single character from
- # the input stream.
- #
- # outproc a procedure that writes a single character (its
- # argument) to the output stream.
- #
- # maxTableSize the maximum size to which the string table
- # is allowed to grow before something is done about it.
- # If the size is positive, the table is discarded and
- # a new one started. If negative, it is retained, but
- # no new entries are added.
- #
-
- procedure compress(inproc,outproc,maxTableSize)
- local EOF,c,charTable,junk1,junk2,outcode,s,t,
- tossTable,x
- #
- # Initialize.
- #
- /maxTableSize := 1024 # 10 "bits"
- every outproc(!string(maxTableSize))
- outproc("\n")
- tossTable := maxTableSize
- /lzw_recycles := 0
- if maxTableSize < 0 then maxTableSize := -maxTableSize
- charTable := table()
- every c := !&cset do charTable[c] := ord(c)
- EOF := charTable[*charTable] := *charTable # reserve code=256 for EOF
- lzw_stringTable := copy(charTable)
- #
- # Compress the input stream.
- #
- s := inproc() | return maxTableSize
- if \lzw_trace then {
- wr(&errout,"\nInput string\tOutput code\tNew table entry")
- wrs(&errout,"\"",image(s)[2:-1])
- }
- while c := inproc() do {
- if \lzw_trace then
- wrs(&errout,image(c)[2:-1])
- if \lzw_stringTable[t := s || c] then s := t
- else {
- compress_output(outproc,junk2 := lzw_stringTable[s],junk1 := *lzw_stringTable)
- if *lzw_stringTable < maxTableSize then
- lzw_stringTable[t] := *lzw_stringTable
- else if tossTable >= 0 then {
- lzw_stringTable := copy(charTable)
- lzw_recycles +:= 1
- }
- if \lzw_trace then
- wrs(&errout,"\"\t\t",
- image(char(*&cset > junk2) | junk2),
- "(",junk1,")\t\t",lzw_stringTable[t]," = ",image(t),"\n\"")
- s := c
- }
- }
- compress_output(outproc,lzw_stringTable[s],*lzw_stringTable)
- if \lzw_trace then
- wr(&errout,"\"\t\t",
- image(char(*&cset > (x := \lzw_stringTable[s] | 0)) | x))
- compress_output(outproc,EOF,*lzw_stringTable)
- compress_output(outproc)
- return maxTableSize
- end
-
-
- procedure compress_output(outproc,code,stringTableSize)
- local outcode
- static max,bits,buffer,bufferbits,lastSize
- #
- # Initialize.
- #
- initial {
- lastSize := 1000000
- buffer := bufferbits := 0
- }
- #
- # If this is "close" call, flush buffer and reinitialize.
- #
- if /code then {
- outcode := &null
- if bufferbits > 0 then
- outproc(char(outcode := ishift(buffer,8 - bufferbits)))
- lastSize := 1000000
- buffer := bufferbits := 0
- return outcode
- }
- #
- # Expand output code size if necessary.
- #
- if stringTableSize < lastSize then {
- max := 1
- bits := 0
- }
- while stringTableSize > max do {
- max *:= 2
- bits +:= 1
- }
- lastSize := stringTableSize
- #
- # Merge new code into buffer.
- #
- buffer := ior(ishift(buffer,bits),code)
- bufferbits +:= bits
- #
- # Output bits.
- #
- while bufferbits >= 8 do {
- outproc(char(outcode := ishift(buffer,8 - bufferbits)))
- buffer := ixor(buffer,ishift(outcode,bufferbits - 8))
- bufferbits -:= 8
- }
- return outcode
- end
-
- ############################################################################
- #
- # decompress() -- LZW decompression of compressed stream created
- # by compress()
- #
- # Arguments:
- #
- # inproc a procedure that returns a single character from
- # the input stream.
- #
- # outproc a procedure that writes a single character (its
- # argument) to the output stream.
- #
-
- procedure decompress(inproc,outproc)
- local EOF,c,charSize,code,i,maxTableSize,new_code,old_strg,
- strg,tossTable
- #
- # Initialize.
- #
- maxTableSize := ""
- while (c := inproc()) ~== "\n" do maxTableSize ||:= c
- maxTableSize := integer(maxTableSize) |
- stop("Invalid file format -- max table size missing")
- tossTable := maxTableSize
- /lzw_recycles := 0
- if maxTableSize < 0 then maxTableSize := -maxTableSize
- maxTableSize -:= 1
- lzw_stringTable := list(*&cset)
- every i := 1 to *lzw_stringTable do lzw_stringTable[i] := char(i - 1)
- put(lzw_stringTable,EOF := *lzw_stringTable) # reserve code=256 for EOF
- charSize := *lzw_stringTable
- if \lzw_trace then
- wr(&errout,"\nInput code\tOutput string\tNew table entry")
- #
- # Decompress the input stream.
- #
- while old_strg :=
- lzw_stringTable[decompress_read_code(inproc,*lzw_stringTable,EOF) + 1] do {
- if \lzw_trace then
- wr(&errout,image(old_strg),"(",*lzw_stringTable,")",
- "\t",image(old_strg))
- outproc(old_strg)
- c := old_strg[1]
- (while new_code := decompress_read_code(inproc,*lzw_stringTable + 1,EOF) do {
- strg := lzw_stringTable[new_code + 1] | old_strg || c
- outproc(strg)
- c := strg[1]
- if \lzw_trace then
- wr(&errout,image(char(*&cset > new_code) \ 1 | new_code),
- "(",*lzw_stringTable + 1,")","\t",
- image(strg),"\t\t",
- *lzw_stringTable," = ",image(old_strg || c))
- if *lzw_stringTable < maxTableSize then
- put(lzw_stringTable,old_strg || c)
- else if tossTable >= 0 then {
- lzw_stringTable := lzw_stringTable[1:charSize + 1]
- lzw_recycles +:= 1
- break
- }
- old_strg := strg
- }) | break # exit outer loop if this loop completed
- }
- decompress_read_code()
- return maxTableSize
- end
-
-
- procedure decompress_read_code(inproc,stringTableSize,EOF)
- local code
- static max,bits,buffer,bufferbits,lastSize
-
- #
- # Initialize.
- #
- initial {
- lastSize := 1000000
- buffer := bufferbits := 0
- }
- #
- # Reinitialize if called with no arguments.
- #
- if /inproc then {
- lastSize := 1000000
- buffer := bufferbits := 0
- return
- }
- #
- # Expand code size if necessary.
- #
- if stringTableSize < lastSize then {
- max := 1
- bits := 0
- }
- while stringTableSize > max do {
- max *:= 2
- bits +:= 1
- }
- #
- # Read in more data if necessary.
- #
- while bufferbits < bits do {
- buffer := ior(ishift(buffer,8),ord(inproc())) |
- stop("Premature end of file")
- bufferbits +:= 8
- }
- #
- # Extract code from buffer and return.
- #
- code := ishift(buffer,bits - bufferbits)
- buffer := ixor(buffer,ishift(code,bufferbits - bits))
- bufferbits -:= bits
- return EOF ~= code
- end
-